package ms.sudoku.technique;

import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

import ms.sudoku.PuzzleState;
import ms.sudoku.util.IntSet;

import org.lex.game.sudoku.model.Point;

import common.Sudokus;

public final class BeginnerTechnique extends EliminationTechnique {
	public BeginnerTechnique() {
	}

	public long getDifficultyLevel() {
		return 1;
	}

	public boolean execute(PuzzleState state, boolean exitEarlyWhenSoleFound, IntSet[][] possibleNumbers,
			AtomicInteger numberOfChanges, AtomicBoolean exitedEarly) {
		numberOfChanges.set(0);
		exitedEarly.set(false);

		int gridSize = state.getGridSize();
		int boxSize = state.getBoxSize();
		int currentNumCount = 0;

		Point[] numLocations = new Point[gridSize];
		for (int currentNum = 0; currentNum < gridSize; currentNum++) {
			/*
			 * Find all occurrences of n. If GridSize (9) or more are found, no
			 * need to continue. Because if more than GridSize are found, there
			 * is at least one mistake somewhere on the board. If fewer than 9
			 * are found, populate locations with a new Point.
			 */
			currentNumCount = 0;
			for (int row = 0; row < gridSize && currentNumCount < gridSize; row++) {
				for (int column = 0; column < gridSize && currentNumCount < gridSize; column++) {
					int num = state.get(row, column);
					if (Sudokus.notEmpty(num) && num == currentNum) {
						numLocations[currentNumCount++] = new Point(row, column);
					}

				}
			}
			if (currentNumCount >= gridSize) {
				continue;
			}

			// For each box
			for (int boxIndex = 0; boxIndex < gridSize; boxIndex++) {
				boolean done = false;
				int boxStartX = (boxIndex % boxSize) * boxSize;
				for (int x = boxStartX; x < boxStartX + boxSize && !done; x++) {
					int boxStartY = (boxIndex / boxSize) * boxSize;
					for (int y = boxStartY; y < boxStartY + boxSize && !done; y++) {
						int num = state.get(x, y);
						if ((Sudokus.notEmpty(num) && num == currentNum)
								|| (Sudokus.isEmpty(num) && possibleNumbers[x][y].size() == 1 && possibleNumbers[x][y]
										.contains(currentNum)))
							done = true;

					}
				}
				if (done) {
					continue;
				}

				// Look at each cell in the box
				Point targetCell = null;
				boxStartX = (boxIndex % boxSize) * boxSize;
				for (int x = boxStartX; x < boxStartX + boxSize && !done; x++) {
					int boxStartY = (boxIndex / boxSize) * boxSize;
					for (int y = boxStartY; y < boxStartY + boxSize && !done; y++) {
						Point cell = new Point(x, y);
						int num = state.get(cell);
						if (Sudokus.notEmpty(num) && possibleNumbers[x][y].contains(currentNum)) {
							boolean invalid = false;
							for (int locNum = 0; locNum < currentNumCount; locNum++) {
								if (numLocations[locNum].x == cell.x || numLocations[locNum].y == cell.y) {
									invalid = true;
									break;
								}

							}
							if (!invalid) {
								if (targetCell == null)
									targetCell = cell;
								else {
									targetCell = null;
									done = true;
								}
							}

						}

					}
				}
				if (targetCell != null) {
					possibleNumbers[targetCell.x][targetCell.y].clear();
					possibleNumbers[targetCell.x][targetCell.y].add(currentNum);
					numberOfChanges.incrementAndGet();
					if (exitEarlyWhenSoleFound) {
						exitedEarly.set(true);
						return false;
					}

				}

			}
		}
		return false;
	}

}
